# @Time : 2021/4/7 9:05
# @Author : Fioman 
# @Phone : 13149920693
"""
程序入口主函数,主要负责主程序的启动
"""
import json
import os
import signal
import sys
import threading

import bottle
import bottle.ext.websocket as wbs
import gevent

import app_config
from app.device_state import DeviceStates
from app.produce_handler import ProduceHandler
from app.state_listenser import StateListener
from common import ps_control
from common.datetime_helper import get_datetime_with_ms
from common.produce_logger import ProduceLogger
from db_tools.db_handler import DbHandler
from drivers.area_camera import AreaCamera
from drivers.cam_zmc import CamZmc
from drivers.cbj_zmc import CbjZmc
from drivers.cutter_zmc import CutterZmc
from drivers.line_camera import LineCamera
from drivers.offline_zmc import OfflineZmc
from eel import eel_manager
from tools.cbj_zmc_tool import CbjTool
from tools.cutter_camera_cal_tool import CutterCamCalTool
from tools.line_camera_cal_tool import LineCameraCalTool
from tools.line_camera_joint_tool import ImageJointTool
from tools.logger_tool import LoggerTool

logger = ProduceLogger()
device_states = DeviceStates()


@bottle.route('/<path:path>')
def _static(path):
    return bottle.static_file(path, root=app_config.web_path)


@bottle.get('/eel', apply=[wbs.websocket])
def _websocket(ws):
    global websockets, web_is_connected
    page = bottle.request.query.page
    websockets += [(page, ws)]
    logger.debug(f"收到web连接请求: {websockets},time = {get_datetime_with_ms()}")
    web_is_connected = True
    try:
        while True:
            msg = ws.receive()
            print("recv msg: ", msg)
            if msg is not None:
                message = json.loads(msg)
                gevent.spawn(_process_message, message)
            else:
                print("websockets = {}".format(websockets))
                websockets.remove((page, ws))
    except Exception as e:
        logger.debug(f"in _websocket() 连接断开: {len(websockets)},异常: {str(e)}")
        web_is_connected = False
        check_web_is_reconnect_thread = threading.Thread(target=check_page_is_close, args=())
        check_web_is_reconnect_thread.setDaemon(True)
        check_web_is_reconnect_thread.start()


def check_page_is_close():
    gevent.sleep(1.5)
    if not web_is_connected:
        logger.debug("检测到网页关闭,关闭后台服务器程序!")
        _websocket_close()
    else:
        print("检测到网页刷新,更新界面数据")
        device_states.web_data_is_init = False
        device_states.web_show_data_init()


def _websocket_close():
    """
    页面关闭的时候,也就是程序关闭的时候,要做的收尾的工作.
    :return:
    """
    logger.debug(f"页面websocket关闭,程序退出,关闭之后的len(websockets) = {len(websockets)}")
    _program_stop()
    gevent.sleep(1)
    pid = os.getpid()
    os.kill(pid, signal.SIGTERM)


def _program_stop():
    """
    程序页面关闭要做的事情
    :return:
    """
    cutter_zmc.cutter_zmc_stop()
    device_states.stop_work_flag = True
    app_config.is_main_alive = False


def _process_message(message):
    """
    解析前端发送过来的消息,并返回.
    :param message:前端发送过来的消息内容
    :return:
    """
    if 'call' in message:
        obj_name = message.get('obj_name', False)
        fun_name = message.get('fun_name', False)
        if obj_name and fun_name:
            if obj_name == 'global':
                if globals().get(fun_name):
                    try:
                        return_val = globals()[fun_name](*message['args'])
                    except Exception as e:
                        return_val = "Call error!\n" + str(e)
                else:
                    return_val = "Call error!Global does not have %s function!" % fun_name
            else:
                if globals().get(obj_name):
                    if fun_name in dir(globals()[obj_name]):
                        if hasattr(getattr(globals()[obj_name], fun_name), '__call__'):
                            try:
                                return_val = getattr(globals()[obj_name], fun_name)(*message['args'])
                            except Exception as e:
                                return_val = "Call error!\n" + str(e)
                        else:
                            return_val = "Call error!%s 's %s is not a function!" % (obj_name, fun_name)
                            logger.debug(return_val)
                    else:
                        return_val = "Call error!%s does not have this %s!" % (obj_name, fun_name)
                        print(return_val)
                        logger.debug(return_val)
                else:
                    return_val = "Call error!Global does not have %s object!" % obj_name
                    logger.debug(return_val)
        else:
            return_val = "Call command syntax error!"
            print(return_val)
            logger.debug(return_val)
        eel_manager.repeat_send(json.dumps({'return': message['call'], 'value': return_val}), websockets)
    else:
        logger.debug(f"Invalid message received: {message}")


if __name__ == '__main__':
    db = DbHandler()
    web_is_connected = False  # web是否连接
    websockets = []  # 存放websocekt的套接字
    if not app_config.is_develop:
        # 如果不是原厂和自己的设备,程序就不启动
        ps_control.verify_device()
        # 程序启动检测,这里先不做
    app_config.is_main_alive = True
    # zmc对象
    offline_zmc = OfflineZmc()
    cam_zmc = CamZmc()
    cbj_zmc = CbjZmc()
    cutter_zmc = CutterZmc()
    # 相机对象初始化,获取相机的Ip地址和其对应的网卡的地址
    result = db.get_camera_ip()
    if result.state:
        # 获取相机的Ip地址,包括两个部分,一个是相机Ip,一个是网卡的Ip地址(就是本地网卡Ip)
        left_ips, middle_ips, right_ips, cutter01_ips, cutter02_ips = result.data
        # 如果是在调试模式下,打印下相机的Ip地址信息
        logger.debug(f"左相机ip: {left_ips}\n中相机ip:{middle_ips}\n右相机ip:{right_ips}\n"
                     f"铡刀1号ip{cutter01_ips}\n铡刀2号ip{cutter02_ips}")
    else:
        logger.debug(result.info)
        sys.exit(0)
    # 然后是初始化相机
    left_cam = LineCamera(*left_ips)
    mid_cam = LineCamera(*middle_ips)
    right_cam = LineCamera(*right_ips)
    cutter_cam01 = AreaCamera(*cutter01_ips)
    cutter_cam02 = AreaCamera(*cutter02_ips)
    image_joint = ImageJointTool(left_cam, mid_cam, right_cam, cam_zmc)
    line_cam_cal = LineCameraCalTool(image_joint, cbj_zmc, cutter_zmc)
    cutter_cam_cal = CutterCamCalTool(cutter_cam01, cutter_cam02, cutter_zmc)
    cbj_tool = CbjTool(cbj_zmc)
    hdlt = LoggerTool()
    ph = ProduceHandler(image_joint, cutter_cam_cal, offline_zmc, cam_zmc, cbj_zmc, cutter_zmc)
    st = StateListener(left_cam, mid_cam, right_cam, cutter_cam01, cutter_cam02, offline_zmc, cam_zmc,
                       cbj_zmc, cutter_zmc)
    st.manager_thread.setDaemon(True)
    st.manager_thread.start()
    logger.debug("程序启动,初始化完成,开始启动web服务器...")
    if not app_config.is_develop:
        res = db.web_reqeust_dump_data_base()
        if res.state:
            logger.debug("数据库备份成功!")
        else:
            logger.debug(res.info)
    eel_manager.start_eel()
    eel_manager.send_loop(websockets)
